﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Qizx.XQuery.Op {
    class SwitchExpr : Expression {
        public Expression switche;
        protected Case[] cases;
        protected bool updating;

        public SwitchExpr(Expression on) {
            this.switche = on;
        }

        public class Case : VarClause {
            public Expression key;

            public Case()
                : base(null) {
            }

            public void dump(ExprDisplay d) {
                d.header(key == null ? "default" : "case");
                d.child("key", key);
                d.child("expr", expr);
            }
        }

        public void addCase(Case clause) {
            if (cases == null)
                cases = new Case[] { clause };
            else {
                Case[] old = cases;
                cases = new Case[old.Length + 1];
                System.Array.Copy(old, 0, cases, 0, old.Length);
                cases[old.Length] = clause;
            }
        }

        Case getCaseClause(int rank) {
            return rank < 0 || rank >= cases.Length
                ? null : cases[rank];
        }

        public override Expression Child(int rank) {
            return (rank == 0) ? switche
                   : (rank <= cases.Length) ? cases[rank - 1] : null;
        }

        public void dump(ExprDisplay d) {
            d.header(this);
            d.child("on", switche);
            d.children(cases);
        }

        public Expression staticCheck(ModuleContext context, int flags) {
            int nonUpCount = 0;
            switche = context.staticCheck(switche, 0);

            type = null;
            for (int c = 0, C = cases.Length; c < C; c++) {
                context.simpleStaticCheck(cases[c], 0);
                if (type == null)
                    type = cases[c].getType();
                else
                    type = type.unionWith(cases[c].getType(), true);

                if (cases[c].updating)
                    updating = true;
                else if (!UpdatingExpr.isVacuous(cases[c].expr))
                    ++nonUpCount;
            }

            // OPTIM
            // detect all keys are constants of same type => map
            // fallback: evaluate keys in order and compare

            if (updating && (nonUpCount > 0))
                context.error("XUST0001", this, "mix of updating and non-updating "
                              + "expressions in typeswitch");
            return this;
        }

        public bool isVacuous() {
            for (int c = 0, C = cases.Length; c < C; c++) {
                if (!UpdatingExpr.isVacuous(cases[c].expr))
                    return false;
            }
            return true;
        }

        public int getFlags() {
            return updating ? UPDATING : 0;
        }

        public XQValue eval(Focus focus, EvalContext context) {
            XQItem value = switche.evalAsItem(focus, context);
            context.at(this);

            // default implementation
            int select = 0, bound = cases.Length - 1;
            for (; select < bound; select++) {
                XQItem caseValue = cases[select].key.evalAsOptItem(focus, context);
                if (caseValue == null)
                    continue;
                int cmp = value.CompareTo(caseValue, context, XQItemCompareType.COMPAR_VALUE);
                if (cmp == 0)
                    break;
            }

            Expression res = cases[select].expr;
            while (res == null && select < bound) {
                ++select;
                res = cases[select].expr;
            }
            return cases[select].expr.eval(focus, context);
        }
    }
}